﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Common;
using System.Threading;

namespace NextNet
{
    public class HandleFile
    {
        public delegate void SendDelegate(SendMessage mess);
        public static event SendDelegate SendHandle;

        #region 发送消息
        public void Send(int clientID, byte[] stream)
        {
            SendHandle?.Invoke(new SendMessage()
            {
                ID = clientID,
                Stream = stream
            });

        }

        public void Send(int clientID, DataType data, MessType type)
        {
            byte[] message = new byte[StreamHead.HeadLength];
            StreamHead.Write(message, clientID, data, type, 0);

            Send(clientID, message);
        }

        public void Send(int clientID, DataType data, MessType type, byte[] stream)
        {
            int length = StreamHead.HeadLength + stream.Length;
            byte[] message = new byte[length];
            StreamHead.Write(message, clientID, data, type, stream.Length);
            Array.Copy(stream, 0, message, StreamHead.HeadLength, stream.Length);

            Send(clientID, message);
        }

        public void Send(int clientID, DataType data, MessType messType, int fileID, byte[] stream)
        {
            byte[] message = new byte[stream.Length + 4];
            Array.Copy(BitConverter.GetBytes(fileID), message, 4);
            Array.Copy(stream, 0, message, 4, stream.Length);

            Send(clientID, data, messType, message);
        }

        public void Send(int clientID, DataType data, MessType messType, int fileID)
        {
            byte[] message = new byte[4];
            Array.Copy(BitConverter.GetBytes(fileID), message, 4);

            Send(clientID, data, messType, message);
        }

        public void Send(int clientID, DataType data, MessType messType, int fileID, long seek)
        {
            byte[] message = new byte[12];
            Array.Copy(BitConverter.GetBytes(fileID), message, 4);
            Array.Copy(BitConverter.GetBytes(seek), 0, message, 4, 8);

            Send(clientID, data, messType, message);
        }

        public void Send(int clientID, DataType data, MessType messType, int fileID, long seek, byte[] stream)
        {
            byte[] message = new byte[12 + stream.Length];
            Array.Copy(BitConverter.GetBytes(fileID), message, 4);
            Array.Copy(BitConverter.GetBytes(seek), 0, message, 4, 8);
            Array.Copy(stream, 0, message, 12, stream.Length);

            Send(clientID, data, messType, message);
        }
        #endregion

        //文件发送队列
        public static List<SenderFile> ListSourceFile = new List<SenderFile>();
        //文件接收队列
        public static List<StreamFile> ListTargetFile = new List<StreamFile>();

        /// <summary>
        /// 发送文件信息
        /// </summary>
        /// <param name="clientID"></param>
        /// <param name="type"></param>
        /// <param name="filePath"></param>
        /// <param name="target"></param>
        public void FileSend(int clientID, string filePath, string target)
        {
            try
            {


                //判断文件夹
                if (Directory.Exists(filePath))
                {
                    if (filePath[filePath.Length - 1] != '\\')
                        filePath += '\\';

                    List<string> fileList = new List<string>();
                    FindFiles findFiles = new FindFiles();
                    findFiles.Find(filePath, ref fileList);

                    int len = filePath.LastIndexOf("\\", filePath.Length - 2);
                    string folder = filePath.Substring(len + 1, filePath.Length - len - 1);

                    foreach (string path in fileList)
                    {
                        FileInfo fileInfo = new FileInfo(path);

                        int fileID = fileInfo.GetHashCode();

                        SenderFile senderFile = new SenderFile()
                        {
                            ID = clientID,
                            FileID = fileID,
                            FilePath = path,
                            Size = fileInfo.Length,
                            Seek = 0,
                            State = FileState.None
                        };

                        lock (ListSourceFile)
                        {
                            ListSourceFile.Add(senderFile);
                        }

                        string folderBlock = path.Substring(filePath.Length, path.Length - filePath.Length - fileInfo.Name.Length);
                        string md5 = Auxiliary.GetFileMD5(path);
                        StreamFile streamFIle = new StreamFile()
                        {
                            ID = clientID,
                            FileID = fileID,
                            Size = fileInfo.Length,
                            Seek = 0,
                            FileName = fileInfo.Name,
                            MD5 = md5,
                            Path = target + folder + folderBlock
                        };

                        Send(clientID, DataType.File, MessType.FileHead, streamFIle.ToStream());
                    }
                }
                //判断文件
                else if (File.Exists(filePath))
                {
                    FileInfo fileInfo = new FileInfo(filePath);

                    int fileID = fileInfo.GetHashCode();

                    SenderFile senderFile = new SenderFile()
                    {
                        ID = clientID,
                        FileID = fileID,
                        FilePath = filePath,
                        Size = fileInfo.Length,
                        Seek = 0,
                        State = FileState.None
                    };

                    lock (ListSourceFile)
                    {
                        ListSourceFile.Add(senderFile);
                    }

                    string md5 = Auxiliary.GetFileMD5(filePath);
                    StreamFile streamFIle = new StreamFile()
                    {
                        ID = clientID,
                        FileID = fileID,
                        Size = fileInfo.Length,
                        Seek = 0,
                        FileName = fileInfo.Name,
                        MD5 = md5,
                        Path = target
                    };
                    Send(clientID, DataType.File, MessType.FileHead, streamFIle.ToStream());
                }
            }
            catch (Exception ex)
            {
                Logfile log = new Logfile(1);
                log.Write(ex.Message, "FileSend");
            }
        }

        /// <summary>
        /// 继续接收文件
        /// </summary>
        /// <param name="fileID"></param>
        public void FileTargetContinue(int clientID, int fileID)
        {
            StreamFile senderFile;
            lock (ListSourceFile)
            {
                senderFile = ListTargetFile.Find((f) => f.FileID == fileID);
            }

            if (senderFile == null) return;

            long size = senderFile.Size;
            long seek = senderFile.Seek;

            // 一次请求多个文件块 收到文件块后会自动请求下一个
            for (int i = 0; i < StreamFile.MaxConcurrentSum && seek + i * StreamFile.BlockMaxSize <= size; i++)
            {
                Send(clientID, DataType.File, MessType.FileSourceBlock, fileID, seek + i * StreamFile.BlockMaxSize);
            }

        }

        /// <summary>
        /// 继续发送文件
        /// </summary>
        /// <param name="fileID"></param>
        public void FileContinue(int fileID)
        {
            lock (ListTargetFile)
            {
                StreamFile streamFile = ListTargetFile.Find((f) => f.FileID == fileID);

                if (streamFile != null && streamFile.State == FileState.Aborted || streamFile.State == FileState.Completed)
                    return;

                Send(streamFile.ID, DataType.File, MessType.FileTargetContinue, fileID);
            }
        }

        /// <summary>
        /// 发送文件数据
        /// </summary>
        /// <param name="clientID"></param>
        /// <param name="fileID"></param>
        /// <param name="seek"></param>
        public void FileReadBolck(int fileID, long seek)
        {
            SenderFile senderFile;
            lock (ListSourceFile)
            {
                senderFile = ListSourceFile.Find((f) => f.FileID == fileID);
            }

            if (senderFile == null)
                return;

            FileState state = senderFile.State;
            if (state == FileState.Aborted || state == FileState.Completed || state == FileState.Transing)
                return;

            long length = senderFile.Size - seek;
            if (length > StreamFile.BlockMaxSize)
                length = StreamFile.BlockMaxSize;

            int clientID = senderFile.ID;
            int blockSize = (int)length;
            byte[] buffer = new byte[blockSize];

            try
            {
                FileStream file = new FileStream(senderFile.FilePath, FileMode.Open);
                file.Seek(seek, SeekOrigin.Begin);
                file.Read(buffer, 0, blockSize);
                file.Close();

                Send(clientID, DataType.File, MessType.FileTargetBlock, fileID, seek, buffer);

                senderFile.Seek = seek + length;
                FileSoureSate(senderFile);
            }
            catch (Exception ex)
            {
                Logfile log = new Logfile(1);
                log.Write(ex.Message, "FileReadBolck");

                Send(clientID, DataType.File, MessType.FileTargetAbort, fileID);
            }



        }

        /// <summary>
        /// 写入文件数据
        /// </summary>
        /// <param name="clientID"></param>
        /// <param name="FileID"></param>
        /// <param name="seek"></param>
        /// <param name="stream"></param>
        public void FileWrite(int clientID, int FileID, long seek, byte[] stream)
        {
            StreamFile streamFile;
            lock (ListTargetFile)
            {
                streamFile = ListTargetFile.Find((f) => f.FileID == FileID);
            }

            if (stream == null)
                return;

            FileState state = streamFile.State;
            if (state == FileState.Aborted || state == FileState.Completed)
                return;

            long size = streamFile.Size;
            int length = stream.Length;
            string path = FillPath(streamFile.Path, streamFile.FileName);

            FileStream file = new FileStream(path, FileMode.Open);

            try
            {
                file.Seek(seek, SeekOrigin.Begin);
                file.Write(stream, 0, length);
                file.Close();
            }
            catch (Exception ex)
            {
                file.Close();
                streamFile.State = FileState.Aborted;

                Send(clientID, DataType.File, MessType.FileSourceAbort, FileID);

                Logfile log = new Logfile(1);
                log.Write(ex.Message, "FileWrite");
            }

            streamFile.Seek += length;

            if (length < StreamFile.BlockMaxSize)
            {
                streamFile.State = FileState.Completed;

                Send(clientID, DataType.File, MessType.FileCompleted, FileID);
            }
            else
            {
                long t = seek + StreamFile.MaxConcurrentSum * StreamFile.BlockMaxSize;
                if (t <= size)
                    Send(clientID, DataType.File, MessType.FileSourceBlock, FileID, t);
            }

        }

        /// <summary>
        /// 创建文件
        /// </summary>
        public void FileCreat(StreamFile streamFile)
        {
            string folder = streamFile.Path;
            if (folder.IndexOf(":") <= 0)
                folder = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "Received\\" + folder;
            string fileName = streamFile.FileName;

            try
            {
                if (!Directory.Exists(folder))
                    Directory.CreateDirectory(folder);

                FileStream file = new FileStream(folder + fileName, FileMode.OpenOrCreate);
                file.Close();

                streamFile.Seek = 0;
                lock (ListTargetFile)
                {
                    ListTargetFile.Add(streamFile);
                }
            }
            catch (Exception ex)
            {
                Logfile log = new Logfile(1);
                log.Write(ex.Message, "FileCreat");

                Send(streamFile.ID, DataType.File, MessType.FileSourceAbort, streamFile.FileID);
            }

        }

        /// <summary>
        /// 暂停文件传输
        /// </summary>
        /// <param name="fileID"></param>
        public void FileTargetPause(int fileID)
        {
            StreamFile streamFile;
            lock (ListTargetFile)
            {
                streamFile = ListTargetFile.Find((f) => f.FileID == fileID);
            }
            if (streamFile == null) return;

            streamFile.State = FileState.Paused;
            FileTargetState(streamFile);
        }

        #region 更新文件状态
        /// <summary>
        /// 更新发送文件状态
        /// </summary>
        /// <param name="senderFile"></param>
        public void FileSoureSate(SenderFile senderFile)
        {
            lock (ListSourceFile)
            {
                for (int i = 0; i < ListSourceFile.Count; i++)
                {
                    if (ListSourceFile[i] == senderFile)
                    {
                        FileState state = ListSourceFile[i].State;
                        if (state == FileState.Aborted || state == FileState.Completed)
                            ListSourceFile.Remove(senderFile);
                        else
                            ListSourceFile[i] = senderFile;
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// 更新发送文件状态
        /// </summary>
        /// <param name="fileID"></param>
        /// <param name="state"></param>
        public void FileSoureSate(int fileID, FileState state)
        {
            SenderFile senderFile;
            lock (ListSourceFile)
            {
                senderFile = ListSourceFile.Find((f) => f.FileID == fileID);
            }
            if (senderFile == null) return;

            senderFile.State = state;
            FileSoureSate(senderFile);
        }

        /// <summary>
        /// 更新接收文件状态
        /// </summary>
        /// <param name="streamFile"></param>
        public void FileTargetState(StreamFile streamFile)
        {
            lock (ListTargetFile)
            {
                for (int i = 0; i < ListTargetFile.Count; i++)
                {
                    if (ListTargetFile[i] == streamFile)
                    {
                        FileState state = ListTargetFile[i].State;
                        if (state == FileState.Aborted || state == FileState.Completed)
                        {
                            ListTargetFile.Remove(streamFile);

                            string path = FillPath(streamFile.Path, streamFile.FileName);

                            FileInfo file = new FileInfo(path);
                            file.Delete();
                        }
                        else
                            ListTargetFile[i] = streamFile;
                        return;
                    }
                }
            }
        }

        /// <summary>
        /// 更新接收文件状态
        /// </summary>
        /// <param name="fileID"></param>
        /// <param name="state"></param>
        public void FileTargetState(int fileID, FileState state)
        {
            StreamFile streamFile;
            lock (ListTargetFile)
            {
                streamFile = ListTargetFile.Find((f) => f.FileID == fileID);
            }

            if (streamFile == null) return;

            streamFile.State = state;
            FileTargetState(streamFile);
        }

        /// <summary>
        /// 补齐路径
        /// </summary>
        /// <param name="path"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public string FillPath(string path, string name)
        {
            if (path.IndexOf(":") <= 0)
                path = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + path;

            return path += name;
        }
        #endregion

        /// <summary>
        /// 处理文件消息
        /// </summary>
        /// <param name="stream"></param>
        /// <param name="id"></param>
        /// <param name="type"></param>
        public void FileDispose(byte[] stream, int id, MessType type)
        {
            if (type == MessType.FileHead)
            {
                StreamFile streamFile = new StreamFile();
                streamFile.Read(stream);
                FileCreat(streamFile);

                //接收到文件消息后，请求文件块
                FileTargetContinue(streamFile.ID, streamFile.FileID);
            }
            if (type == MessType.FileTargetBlock)
            {
                int fileID = BitConverter.ToInt32(stream, 0);
                long seek = BitConverter.ToInt64(stream, 4);

                int length = stream.Length - 12;
                byte[] temp = new byte[length];
                Array.Copy(stream,12,temp,0,length);

                FileWrite(id, fileID, seek, temp);
            }
            if (type == MessType.FileTargetAbort)
            {
                int fileID = BitConverter.ToInt32(stream, 0);

                FileTargetState(fileID, FileState.Aborted);
            }
            if (type == MessType.FileTargetPause)
            {
                int fileID = BitConverter.ToInt32(stream, 0);

                FileTargetState(fileID, FileState.Paused);
            }
            if (type == MessType.FileTargetContinue)
            {
                int fileID = BitConverter.ToInt32(stream, 0);
                FileTargetContinue(id, fileID);
            }
            if (type == MessType.FileCompleted)
            {
                int fileID = BitConverter.ToInt32(stream, 0);

                FileTargetState(fileID, FileState.Paused);
            }

            if (type == MessType.FileSourceAbort)
            {
                int fileID = BitConverter.ToInt32(stream, 0);
                FileSoureSate(fileID, FileState.Aborted);
            }
            if (type == MessType.FileSourcePause)
            {
                int fileID = BitConverter.ToInt32(stream, 0);
                FileSoureSate(fileID, FileState.Paused);
            }
            if (type == MessType.FileSourceContinue)
            {
                int fileID = BitConverter.ToInt32(stream, 0);
                FileSoureSate(fileID, FileState.Transing);
            }
            if (type == MessType.FileSourceBlock)
            {
                int fileID = BitConverter.ToInt32(stream, 0);
                long seek = BitConverter.ToInt64(stream, 4);
                FileReadBolck(fileID, seek);
            }
        }
    }
}
